#include "link.h"
link::link(){
	setFlag(QGraphicsItem::ItemIsSelectable, true);
	visPart = new QGraphicsPathItem(this);
	visPart->setZValue(-1);
	setPen(QPen(QBrush(QColor(0,0,0,0)), 15));
	annotation = new diagramTextItem(QPointF(0,0),name,this);
	annotation->setFlag(QGraphicsItem::ItemIsMovable, true);
	
}
link::link(const link& lnk){
}
link::~link(){
	delete from;
	delete annotation;
	delete visPart;
}
QPainterPath  link::drawTunnel(end *e){
	QPainterPath res(e->s->p1);
	QPointF p;
	QPolygonF bracket;
	if (e->type != Output){
		res.moveTo(e->s->p1);
		p = e->s->p1;
		}
	else{
		res.moveTo(e->s->p2);
		p = e->s->p2;
	}
	if (e->s->direction() == segment::Down || e->s->direction() == segment::Up)
	{
		bracket << p+QPointF(-4,-8) 
					<< p+QPointF(-6,-8) 
					<< p+QPointF(-6,8) 
					<< p+QPointF(-4,8);
			res.addPolygon(bracket);
			bracket.clear();
						
			bracket << p+QPointF(4,-8) 
					<< p+QPointF(6,-8) 
					<< p+QPointF(6,8) 
					<< p+QPointF(4,8);
			res.addPolygon(bracket);
	}
	if (e->s->direction() == segment::Left || e->s->direction() == segment::Right)
	{
		bracket		<< p+QPointF(-8,-4) 
					<< p+QPointF(-8,-6) 
					<< p+QPointF(8,-6) 
					<< p+QPointF(8,-4);
			res.addPolygon(bracket);
			bracket.clear();
						
			bracket	<< p+QPointF(-8,4) 
					<< p+QPointF(-8,6) 
					<< p+QPointF(8,6) 
					<< p+QPointF(8,4);
			res.addPolygon(bracket);

	}
	return res;
}
void link::paint( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget){
	QStyleOptionGraphicsItem myoption = (*option);
	if (myoption.state.testFlag(QStyle::State_Selected)){
		myoption.state &= !QStyle::State_Selected;
		visPart->setPen(QPen(Qt::DashLine));
	}
	else if (visPart->pen().style() != Qt::SolidLine)
		visPart->setPen(QPen(Qt::SolidLine));

	 QGraphicsPathItem::paint(painter, &myoption, widget);
}

bool segment::canMoveEndTo(QPointF pos){
	segment *test = new segment(p1);
	bool res = true;
	if (prev != NULL){
		test->p1 = prev->p1;
		test->p2 = prev->p2;
	}
	test = test->extendTo(pos);
	if ((test->prev == NULL) || (test->len() < STEP))
		res = false;
	delete test;
	return res;
}
link::end *link::getEndBySegment(segment *s){
	if (from->s == s)
		return from;
	else
		for (int i = 0; i < to.values().size(); i++)
			if (to.values()[i]->s == s)
				return to.values()[i];
}
link::end *link::findParent(link *l){
	if (from->next == l)
		return from;
	else
		for (int i = 0; i < to.values().size(); i++)
			if (to.values()[i]->next == l)
				return to.values()[i];
}
segment::segment(segment *previous){
	prev = previous; 
	previous->next.append(this); 
	p1=prev->p2;
}
segment::~segment(){
	for(int i =0; i < next.size(); i++) 
		delete next[i];
}
void segment::deleteBranch(segment *end){
	segment *parent	= end->prev;
	segment *child	= end;
	if (parent != 0){
	while ((parent->next.size() < 2) && (parent->prev!=NULL)){	//Ищем место ветвелния
		child = parent;
		parent = parent->prev;
	}
	parent->next.removeAll(child);
	}
	delete child;
}
segment * segment::extendFrom(QPointF pos){

	if(p2.x() == p1.x()){		//Если сегмент - вертикальный
		p1.setY(pos.y());		//Вытягиваем сегмент до точки изгиба линии		
		if(abs(pos.x() - p1.x()) <= 0.1){	//Если конечная точка лежит на продолжении линии сегмента
			 return this;
		}
	}
	else if (p2.y() == p1.y()){	//Если сегмент - горизонтальный
		p1.setX(pos.x());		//Вытягиваем сегмент до точки изгиба линии
		if (abs(pos.y() - p1.y()) <= 0.1){	//Если конечная точка лежит на продолжении линии сегмента
			 return this;
		}
	}


	segment *res = new segment(p1);
	res->p2 = p1+gap();
	
	res->prev = prev;
	if (prev!=NULL)
		res->prev->next[res->prev->next.indexOf(this)] = res;
	if (!next.empty())
		res = res->extendTo(next[0]->p1); 
	else
		res = res->extendTo(p2); 
	res->next = next;
	next.clear();
	for (int j = 0; j < res->next.size(); j++){
		res->next[j]->prev = res;
	}
	if (res->prev == prev)
		return res;
	else
		return res->prev;
}
segment *segment::extendTo(QPointF end){
	segment *seg;
	if(p2.x() == p1.x()){		//Если сегмент - вертикальный
		p2.setY(end.y());		//Вытягиваем сегмент до точки изгиба линии		
		if(abs(end.x() - p2.x()) <= STEP){	//Если конечная точка лежит на продолжении линии сегмента
			 return this;
		}
	}
	else if (p2.y() == p1.y()){	//Если сегмент - горизонтальный
		p2.setX(end.x());		//Вытягиваем сегмент до точки изгиба линии
		if (abs(end.y() - p2.y()) <= STEP){	//Если конечная точка лежит на продолжении линии сегмента
			 return this;
		}
	}
	seg = new segment(this);	//От точки поворота в конечную точку проводим новый сегмент
	seg->p2=end;	
	return seg;
}
QPainterPath segment::formPath(){
/*
	Графический конец(ГК) - точка, где сегмент переходит в кривую поворота, находится на расстоянии STEP от ФК
	Фактический конец(ФК) - точка, где кончался бы сегмент, если бы не было сглаживания, совпадает с ФН следущего сегмента
	Графическое начало(ГН) - точка, где сегмент выходит из кривой поворота, находится на расстоянии STEP от ФН
	Фактическое начало(ФН) - Аналогично ФК, совпадает с ФК предыдущего сегмента
*/
	QPainterPath path;		//путь стрелки
	QPolygonF arrowhead;	//наконечник стрелки
	//Если это не начальный сегмент, начинаем рисовать с графического конца предыдущего сегмента
	if(  !isRoot() ){
		if( prev->direction()!=direction() ) {;	
			//Получаем координаты графического конца
			path.moveTo( p1 - prev->gap());
			//Рисуем поворот до ГН этого сегмента, опорная точка - ФН
			path.quadTo( p1, p1 + gap() );	
		}
		else path.moveTo(p1-gap());		
	}
	else path.moveTo(p1);		//Если это начальный сегмент, но начинаем с его ФН	
	if(!isLeaf()){	
		//Если есть другие сегменты далее, заканчиваем сегмент в ГК, чтобы потом построить поворот
		path.lineTo(p2 - gap());
	}
	else 
		path.lineTo(p2);	//Иначе профодим линию до ФК
	for (int i = 0; i < next.size(); i++){
		//Добавляем остальные сегменты рекуррентно
		path.addPath(next[i]->formPath());
	}
	if (isLeaf())
	{									//Рисуем стрелку
			if (direction() == Right)	
				arrowhead << QPointF(p2.x()-8,p2.y()-3) << QPointF(p2.x(),p2.y()) << QPointF(p2.x()-8,p2.y()+3);
			if (direction() == Left)	
				arrowhead << QPointF(p2.x()+8,p2.y()-3) << QPointF(p2.x(),p2.y()) << QPointF(p2.x()+8,p2.y()+3);
			if (direction() == Down)	
				arrowhead << QPointF(p2.x()-3,p2.y()-8) << QPointF(p2.x(),p2.y()) << QPointF(p2.x()+3,p2.y()-8);
			if (direction() == Up)		
				arrowhead << QPointF(p2.x()-3,p2.y()+8) << QPointF(p2.x(),p2.y()) << QPointF(p2.x()+3,p2.y()+8);
			path.addPolygon(arrowhead);
	}
	return path;
}
QPointF segment::gap(){
	if(len() >= 2*STEP)
		switch(direction()){
				case Up:	return QPointF(0,-STEP);
				case Down:	return QPointF(0,STEP);
				case Left:	return QPointF(-STEP,0);
				case Right: return QPointF(STEP,0);
			}
	else
		switch(direction()){
				case Up:	return QPointF(0,-len()/2);
				case Down:	return QPointF(0,len()/2);
				case Left:	return QPointF(-len()/2,0);
				case Right: return QPointF(len()/2,0);
			}
	return QPointF();
}
segment::Direction segment::direction(){
		if (p2.y()>p1.y())			//Сверху вниз
			return segment::Down;
		else if (p2.y()<p1.y())		//Cнизу вверх
			return segment::Up;
		else if (p2.x()>p1.x())		//Слева направо
			return segment::Right;
		else return segment::Left;	//Справа налево			
}

segment *segment::closestTo(QPointF pos, float *R){

	segment *res = NULL;
	segment *c_res = NULL;
	float r; 
	switch (direction()){
		case Up:
		case Down:
			r = abs(p1.x()-pos.x());
			if( !( (pos.y()>p1.y() && pos.y()<p2.y()) 
				|| (pos.y()>p2.y() && pos.y()<p1.y()) ) )
			r+= qMin(abs(p1.x()-pos.x()),abs(p2.x()-pos.x()));
		break;
		case Left:
		case Right:
			r = abs(p1.y()-pos.y());
			if( !( (pos.x()>p1.x() && pos.x()<p2.x()) 
				|| (pos.x()>p2.x() && pos.x()<p1.x()) ) )
			r+= qMin(abs(p1.y()-pos.y()),abs(p2.y()-pos.y()));
		break;
	}
	if (R != 0){
		qDebug() << "Positiont:" << pos << "Segment:" << QLineF(p1,p2) << "r:" << r << "R:" << *R;
		if (r<*R){
			*R = r;
			res = this;
		}
	}
	else{	
		R = new float;
		*R = r;
		res = this;
	}

	for(int i = 0; i < next.size() && c_res==NULL; i++)
			c_res = next[i]->closestTo(pos,R);
	if (c_res != NULL)
		res =c_res;
	return res;
}





void segment::swap(segment *_old, segment *_new){
	_new = _new->extendTo(_old->p2);
	_new->next = _old->next;
	for (int i = 0 ; i < _new->next.size(); i++){
		_new->next[i]->prev = _new;
	}
}